{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "hamming78.ipynb",
      "provenance": [],
      "collapsed_sections": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7TM86YdFURjU"
      },
      "source": [
        "Copyright 2021 Google LLC\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "you may not use this file except in compliance with the License.\n",
        "You may obtain a copy of the License at\n",
        "\n",
        "    https://www.apache.org/licenses/LICENSE-2.0\n",
        "\n",
        "Unless required by applicable law or agreed to in writing, software\n",
        "distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "See the License for the specific language governing permissions and\n",
        "limitations under the License.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NYRV6qPh8_3Y"
      },
      "source": [
        "## Numerical Evidence for claims presented in the article:\n",
        "### \"From Binary Error Correcting Codes to a Relation Between Maximal D=4 and D=3 Supergravities\""
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "NzMg0SqLWmjp",
        "cellView": "form"
      },
      "source": [
        "#@title Initialization (Simply run this cell to use the default library)\n",
        "reset_library = True #@param {type:\"boolean\"}\n",
        "m_theory_library_url = \"https://github.com/google-research/google-research/trunk/m_theory\" #@param {type:\"string\"}\n",
        "m_theory_library_source = \"Embedded Version\" #@param [\"Embedded Version\", \"Upload from Computer\", \"Fetch from Repository\"]\n",
        "\n",
        "import glob\n",
        "import os\n",
        "import shlex\n",
        "import shutil\n",
        "import subprocess\n",
        "import time\n",
        "\n",
        "\n",
        "def _get_embedded_archive(filename='m_theory.zip'):\n",
        "  import base64\n",
        "  archive = b'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'\n",
        "  with open(filename, 'wb') as h_out:\n",
        "    h_out.write(base64.b64decode(archive))\n",
        "  subprocess.call(['unzip', filename])\n",
        "\n",
        "\n",
        "def _get_archive_via_upload(filename='m_theory.zip'):\n",
        "  from google.colab import files\n",
        "  while True:\n",
        "    print('Please upload', filename)\n",
        "    uploaded = files.upload()\n",
        "    print('Uploaded:', {k: len(v) for k, v in uploaded.items()})\n",
        "    if filename in uploaded:\n",
        "      break\n",
        "  subprocess.call(['unzip', filename])\n",
        "\n",
        "\n",
        "def _get_archive_from_repository(url=None):\n",
        "  import os\n",
        "  import shlex\n",
        "  if url is None:\n",
        "    url = m_theory_library_url\n",
        "  os.system('[ -f /usr/bin/svn ] || apt install subversion')  # Subshell!\n",
        "  subprocess.run(['/usr/bin/svn', 'export', url])\n",
        "\n",
        "###\n",
        "for filename in glob.glob('*.zip'):\n",
        "  print('Removing:', filename)\n",
        "  os.unlink(filename)\n",
        "\n",
        "if reset_library:\n",
        "  try:\n",
        "    shutil.rmtree('m_theory')\n",
        "  except OSError:\n",
        "    pass\n",
        "\n",
        "while not os.access('m_theory', os.X_OK):\n",
        "  time.sleep(0.5)  # If something goes wrong here, do not fast-loop-on-failure.\n",
        "  print('Obtaining package...')\n",
        "  if m_theory_library_source.startswith('Embedded '):\n",
        "    _get_embedded_archive()\n",
        "  elif m_theory_library_source.startswith('Upload '):\n",
        "    _get_archive_via_upload()\n",
        "  elif m_theory_library_source.startswith('Fetch '):\n",
        "    _get_archive_from_repository(m_theory_library_url)\n",
        "  else:\n",
        "    print('Mangled m_theory_library_source, aborting.')\n",
        "    break\n",
        "\n",
        "if os.access('m_theory', os.X_OK):\n",
        "  print('Archive is available.')\n",
        "  # Symlink things upwards into the current directory.\n",
        "  for filename in sorted(os.listdir('m_theory')):\n",
        "    try:\n",
        "      os.symlink(os.path.join('m_theory', filename), filename)\n",
        "    except OSError:\n",
        "      pass"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Ht-95OVokO71",
        "cellView": "form"
      },
      "source": [
        "#@title General Definitions (\"Right-Click -> Form -> Show Code\" to show/hide)\n",
        "\n",
        "# Adjusting import path\n",
        "import os\n",
        "import sys\n",
        "m_theory_path = os.path.join(os.getcwd(), 'm_theory')\n",
        "if m_theory_path not in sys.path: sys.path.append(m_theory_path)\n",
        "\n",
        "\n",
        "from dim3.so8xso8.src import analysis as analysis8\n",
        "from dim4.so8.src import analysis as analysis7\n",
        "\n",
        "from m_theory_lib import algebra\n",
        "from m_theory_lib import m_util as mu\n",
        "\n",
        "import itertools\n",
        "\n",
        "import numpy\n",
        "import tensorflow as tf\n",
        "\n",
        "\n",
        "e7 = algebra.g.e7\n",
        "e8 = algebra.g.e8\n",
        "sugra7c = analysis7.SO8_SUGRA()\n",
        "sugra8 = analysis8.SO8xSO8_SUGRA()\n",
        "\n",
        "\n",
        "# Names are mostly aligned with the mathematics even where this violates PEP-8.\n",
        "# pylint:disable=invalid-name\n",
        "\n",
        "\n",
        "def printpoly(monomials, coeffs, eps=1e-6):\n",
        "  \"\"\"Prints polynomial coefficients.\"\"\"\n",
        "  coeffs = numpy.asarray(coeffs)\n",
        "  coeffs_c = coeffs.reshape(2, -1).T.dot([1, 1j])\n",
        "  rows = []\n",
        "  for mm, p in zip(monomials, coeffs_c):\n",
        "    if abs(p) < eps:\n",
        "      continue\n",
        "    rows.append(\n",
        "        (''.join(str(n + 1) for n, power in enumerate(mm) if power == 1),\n",
        "         numpy.array(p).round(8)[()]))\n",
        "  print(''.join(repr(row) + '\\n'\n",
        "                for row in sorted(rows,\n",
        "                                  key=lambda sc: (-len(sc[0]), sc[0]))))\n",
        "\n",
        "\n",
        "def v14_from_7z(zs):\n",
        "  \"\"\"Maps complex SL2x7 coordinates to the corresponding 14-vector.\"\"\"\n",
        "  cs = numpy.array([0.25 * mu.undiskify(z) for z in zs])\n",
        "  return numpy.concatenate([cs.real, cs.imag], axis=0)\n",
        "\n",
        "\n",
        "def v70_from_7z(zs):\n",
        "  \"\"\"Maps complex SL2x7 coordinates to the corresponding E7 70-vector.\"\"\"\n",
        "  return e7.sl2x7[:2, :, :70].reshape(-1, 70).T.dot(v14_from_7z(zs))\n",
        "\n",
        "\n",
        "def get_v128_from_v70():\n",
        "  \"\"\"Returns the [128, 70]-ndarray mapping an E7 v70 to an E8 v128.\"\"\"\n",
        "  v128_from_v70_expanded = numpy.zeros([2, 8, 8, 2, 35])\n",
        "  for sc in (0, 1):\n",
        "    for d in range(7):\n",
        "      v128_from_v70_expanded[sc, d, d, sc, d] += 1.0\n",
        "      v128_from_v70_expanded[sc, d + 1, d + 1, sc, d] += -1.0\n",
        "    for ij, (i, j) in enumerate(algebra.g.su8.ij_map):\n",
        "      v128_from_v70_expanded[sc, i, j, sc, 7 + ij] += 1.0\n",
        "      v128_from_v70_expanded[sc, j, i, sc, 7 + ij] += 1.0\n",
        "  return v128_from_v70_expanded.reshape(128, 70)\n",
        "\n",
        "\n",
        "v128_from_v70 = get_v128_from_v70()\n",
        "\n",
        "\n",
        "hamming844_words = (\n",
        "    '',\n",
        "    '1248', '1578', '1368', '2358', '2678', '3478', '4568',\n",
        "    '1237', '1256', '1467', '3567', '2457', '1345', '2346',\n",
        "    '12345678')\n",
        "hamming844_terms = [tuple(int(d) - 1 for d in word)\n",
        "                    for word in hamming844_words]\n",
        "hamming844_mask = numpy.stack(\n",
        "    [[x in term for x in range(8)] for term in hamming844_terms],\n",
        "    axis=0)\n",
        "hamming743_mask = hamming844_mask[:, :7]\n",
        "\n",
        "\n",
        "def w_hamming8(zs):\n",
        "  \"\"\"Returns the holomorphic (8,4,4)-Hamming Superpotential.\"\"\"\n",
        "  return sum(zs[sel].prod() for sel in hamming844_mask)\n",
        "\n",
        "\n",
        "def w_hamming7c(zs, omega=0):\n",
        "  \"\"\"Returns the omega-deformed (7,4,3)-Hamming Superpotential.\"\"\"\n",
        "  # We multiply all z8-containing terms with exp(-2jw) and then\n",
        "  # all terms with exp(jw).\n",
        "  return w_hamming8(\n",
        "      numpy.concatenate([zs, [numpy.exp(-2j * omega)]], axis=0)\n",
        "      ) * numpy.exp(1j * omega)\n",
        "\n",
        "\n",
        "def get_tf_pot78c_stat78c(s88=0.0, c88=0.0, squash=False, verbose=False):\n",
        "  \"\"\"Returns (dyonic_potential_func, dyonic_stationarity_func) TF functions.\"\"\"\n",
        "  v128_sc = numpy.stack([numpy.eye(8) * s88, numpy.eye(8) * c88],\n",
        "                        axis=0).reshape(-1)\n",
        "  scale = sugra8.potential_and_stationarity(v128_sc)[0] / (-6)\n",
        "  if verbose:\n",
        "    print(f'scale={scale}')\n",
        "  tc_v128_sc = mu.tff64(v128_sc)\n",
        "  tc_v128_from_v70 = mu.tff64(v128_from_v70)\n",
        "  tc_scale = mu.tff64(scale)\n",
        "  def tf_pot78c(t_v70):\n",
        "    t_v128 = tc_v128_sc + tf.einsum('ea,a->e', tc_v128_from_v70, t_v70)\n",
        "    t_pot, *_, t_stat = sugra8.tf_ext_sugra_tensors(t_v128)\n",
        "    if verbose:\n",
        "      print(f'[E8] t_pot={t_pot.numpy():.12f} t_stat={t_stat.numpy():.12g}')\n",
        "    return t_pot / tc_scale\n",
        "  tf_grad_pot78c = mu.tf_grad(tf_pot78c)\n",
        "  def tf_stat78c(t_v70):\n",
        "    t_pot = tf_pot78c(t_v70)\n",
        "    t_grad = tf_grad_pot78c(t_v70)\n",
        "    t_stat = tf.math.reduce_sum(tf.math.square(t_grad))\n",
        "    if verbose:\n",
        "      print(f'[E7] t_pot={t_pot.numpy():.12f} t_stat={t_stat.numpy():.12g}')\n",
        "    return tf.math.asinh(t_stat) if squash else t_stat\n",
        "  return tf_pot78c, tf_stat78c\n",
        "\n",
        "\n",
        "### Superpotentials for sugra7c and sugra8 ###\n",
        "\n",
        "# Our embedding of SL(2)x7 is such that the superpotential can be read off\n",
        "# of A1 along this direction.\n",
        "sugra7_dir_A1 = numpy.array([0.0, 0.0, 1.0] + [0.0] * 5)\n",
        "\n",
        "\n",
        "def sugra7c_holomorphic_superpotential(zs, omega=0):\n",
        "  \"\"\"The holomorphic superpotential for dyonic-SO(8) supergravity.\"\"\"\n",
        "  zs = numpy.asarray(zs)\n",
        "  v70 = v70_from_7z(zs)\n",
        "  t_vielbein = sugra7c.tf_vielbein(mu.tff64(v70))\n",
        "  # The definitions in this m_theory library differ from\n",
        "  # https://arxiv.org/abs/1302.6219 by omega -> -omega,\n",
        "  # which we here fix by providing -omega:\n",
        "  t_T = sugra7c.tf_T(t_vielbein, t_omega=mu.tff64(-omega))\n",
        "  ts_A123 = sugra7c.tf_A123(t_T, want_A1=True, want_A2=False, want_A3=False)\n",
        "  t_A1, *_ = ts_A123\n",
        "  A1 = t_A1.numpy()\n",
        "  W = sugra7_dir_A1.dot(A1.dot(sugra7_dir_A1))\n",
        "  kaehler_factor = numpy.sqrt((1 - zs * zs.conjugate()).prod())\n",
        "  return W * kaehler_factor\n",
        "\n",
        "\n",
        "sugra8_dir_A1 = numpy.array([0.0] * 15 + [1.0])\n",
        "\n",
        "\n",
        "def embed_70_2_in_128(v70, s, c):\n",
        "  \"\"\"The linear embedding function E(v70, s, c) from the paper.\"\"\"\n",
        "  return 2 * (v128_from_v70.dot(v70) - 0.5 *\n",
        "              numpy.stack([numpy.eye(8) * s,\n",
        "                           numpy.eye(8) * c],\n",
        "                          axis=0).ravel())\n",
        "\n",
        "\n",
        "def sugra8_holomorphic_superpotential(zs):\n",
        "  \"\"\"The holomorphic superpotential for SO(8)xSO(8) supergravity.\"\"\"\n",
        "  zs = numpy.asarray(zs)\n",
        "  v70 = v70_from_7z(zs[:7])\n",
        "  c8 = 0.25 * mu.undiskify(zs[-1])\n",
        "  v128 = embed_70_2_in_128(v70, c8.real, c8.imag)\n",
        "  t_vielbein = sugra8.tf_vielbein(mu.tff64(v128))\n",
        "  t_pot, t_T, t_A1, t_A2 = sugra8.tf_sugra_tensors_from_vielbein_batched(\n",
        "      t_vielbein)\n",
        "  del t_pot, t_T, t_A2  # Unused, named for documentation only.\n",
        "  A1 = t_A1.numpy()\n",
        "  W = sugra8_dir_A1.dot(A1.dot(sugra8_dir_A1))\n",
        "  kaehler_factor = numpy.sqrt((1 - zs * zs.conjugate()).prod())\n",
        "  return W * kaehler_factor\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "MMJJBM1TTcna",
        "cellView": "form"
      },
      "source": [
        "\n",
        "#@title Code for claims 1,2,4\n",
        "\n",
        "# Monomials and points for mapping out the (7,4,3)-Hamming potential.\n",
        "# We assume that we will have max power 1, but have to validate\n",
        "# that later.\n",
        "h7_monomials = numpy.array(list(itertools.product(*[[0.0, 1.0]]*7)))\n",
        "h7_zs = numpy.array(list(itertools.product(*[[-0.1, 0.2]]*7)))\n",
        "\n",
        "\n",
        "# Likewise for the (8,4,4)-Hamming potential.\n",
        "h8_monomials = numpy.array(list(itertools.product(*[[0.0, 1.0]]*8)))\n",
        "h8_zs = numpy.array(\n",
        "    list(itertools.product(*[[-0.15, 0.17]]*8)))\n",
        "\n",
        "\n",
        "def check_superpotential_h7c(omega=0.0, num_spot_checks=20, seed=0,\n",
        "                             verbose=True):\n",
        "  \"\"\"Checks that the (7,4,3)-polynomial matches the D=4 superpotential.\"\"\"\n",
        "  h7_ys = numpy.array([\n",
        "      sugra7c_holomorphic_superpotential(zs, omega)\n",
        "      for zs in h7_zs])\n",
        "  h7_residuals, h7_coeffs, f_fit_h7 = mu.polyfit(\n",
        "      h7_monomials, h7_zs, h7_ys)\n",
        "  assert abs(h7_residuals) < 1e-10\n",
        "  if omega == 0:\n",
        "    # All coeffs need to be ~0 or ~1 at omega==0.\n",
        "    assert set(h7_coeffs.round(5)) <= {0.0, 1.0}\n",
        "  #\n",
        "  print(f'### Polynomial Coefficients for (7,4,3), omega={omega:.8f}, '\n",
        "        f'cos(omega)={numpy.cos(omega):.8f}, '\n",
        "        f'sin(omega)={numpy.sin(omega):.8f} ###')\n",
        "  printpoly(h7_monomials, h7_coeffs)\n",
        "  #\n",
        "  random_z7s = mu.rng(seed).normal(size=[num_spot_checks, 7, 2],\n",
        "                                   scale=0.1).dot([1, 1j])\n",
        "  for rz7s in random_z7s:\n",
        "    predicted = f_fit_h7(rz7s)\n",
        "    target = w_hamming7c(rz7s, omega)\n",
        "    if verbose:\n",
        "      ptd = numpy.array([predicted, target, abs((predicted-target) / target)])\n",
        "      print(f'[predicted, target, delta] = {ptd.round(8).tolist()}')\n",
        "    assert abs(predicted - target) < 1e-10, f'Deviation: {predicted-target:.6g}'\n",
        "  # If we reached this point without tripping over any `assert` checks,\n",
        "  # everything is good.\n",
        "  print('\\n### All checks succeeded. ###\\n')\n",
        "  #\n",
        "  return h7_coeffs, f_fit_h7\n",
        "\n",
        "\n",
        "def check_superpotential_h8(num_spot_checks=20, seed=0,\n",
        "                            verbose=True):\n",
        "  \"\"\"Checks that the (8,4,4)-polynomial matches the D=3 superpotential.\"\"\"\n",
        "  h8_ys = numpy.array([\n",
        "      sugra8_holomorphic_superpotential(zs)\n",
        "      for zs in h8_zs])\n",
        "  h8_residuals, h8_coeffs, f_fit_h8 = mu.polyfit(\n",
        "      h8_monomials, h8_zs, h8_ys)\n",
        "  assert abs(h8_residuals) < 1e-10\n",
        "  assert set(h8_coeffs.round(5)) <= {0.0, 1.0}  # All 0 or 1.\n",
        "  #\n",
        "  print('### Polynomial Coefficients for (8,4,4) ###')\n",
        "  printpoly(h8_monomials, h8_coeffs)\n",
        "  #\n",
        "  random_z8s = mu.rng(seed).normal(size=[num_spot_checks, 8, 2],\n",
        "                                   scale=0.2).dot([1, 1j])\n",
        "  for rz8s in random_z8s:\n",
        "    predicted = f_fit_h8(rz8s)\n",
        "    target = w_hamming8(rz8s)\n",
        "    if verbose:\n",
        "      ptd = numpy.array([predicted, target, abs((predicted-target) / target)])\n",
        "      print(f'[predicted, target, delta] = {ptd.round(8).tolist()}')\n",
        "    assert abs(predicted - target) < 1e-6\n",
        "  # If we reached this point without tripping over any `assert` checks,\n",
        "  # everything is good.\n",
        "  print('\\n### All checks succeeded. ###\\n')\n",
        "  return h8_coeffs, f_fit_h8\n",
        "\n",
        "\n",
        "def do_validate_claim1(verbose=False):\n",
        "  \"\"\"Validates Claim #1.\"\"\"\n",
        "  print('\\n\\n ### C#1: Validating SO(8) superpotential claim. ###\\n\\n')\n",
        "  # Set `verbose=True` for more detail.\n",
        "  check_superpotential_h7c(omega=0, verbose=verbose)\n",
        "\n",
        "\n",
        "def do_validate_claim2(verbose=False):\n",
        "  \"\"\"Validates Claim #2.\"\"\"\n",
        "  print('\\n\\n### C#2: Validating SO(8)xSO(8) superpotential claim. ###\\n\\n')\n",
        "  check_superpotential_h8(verbose=verbose)\n",
        "\n",
        "\n",
        "def do_validate_claim4(verbose=False,\n",
        "                       angles=(0.01, 0.125, 0.2345, -5.678)):\n",
        "  \"\"\"Validates Claim #4.\"\"\"\n",
        "  print('\\n\\n ### C#4: Validating SO(8)c superpotential claim. ###\\n\\n')\n",
        "  for angle in angles:\n",
        "    check_superpotential_h7c(omega=numpy.pi*angle, verbose=verbose)\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "id": "9Bccd6XITwxk"
      },
      "source": [
        "#@title Code for claim 3\n",
        "\n",
        "# For each 8-bit number, we want to know how many 1-bits it contains.\n",
        "num_bits_set = numpy.array(\n",
        "    # [2:] strips the '0b'-prefix.\n",
        "    [sum(map(int, bin(x)[2:])) for x in range(256)])\n",
        "\n",
        "\n",
        "# We encode the Spin(16)-roots as 8-bit numbers,\n",
        "# where +1/2 is represented by a 1-bit and -1/2 by a 0-bit,\n",
        "# the 'leading' bit (that determines whether a root is positive)\n",
        "# has weight 128, and the 'final' bit has weight 1.\n",
        "# We are only interested in positive roots,\n",
        "\n",
        "e8_spin16_pos_roots = [\n",
        "    x for x in range(128, 256) if num_bits_set[x] % 2 == 0]\n",
        "\n",
        "\n",
        "def admissible_root(pos_roots_picked, r_cand):\n",
        "  \"\"\"Returns True iff a root is admissible.\"\"\"\n",
        "  # An admissible positive root\n",
        "  # - is not among the already picked roots\n",
        "  #   (i.e. number of bits different from a picked root\n",
        "  #   is never zero).\n",
        "  # - does not have exactly 2 bits in common\n",
        "  #   (i.e. 6 bits different) with any picked root.\n",
        "  # - does not have exactly 2 bits in common with\n",
        "  #   the negative root associated with a picked positive root\n",
        "  # - (since it is a positive root, it cannot be among the\n",
        "  #    negative roots)\n",
        "  return (all(num_bits_set[r_cand ^ r_picked] not in (0, 6)\n",
        "              for r_picked in pos_roots_picked) and\n",
        "          all(num_bits_set[r_cand ^ (0xff - r_picked)] != 6\n",
        "              for r_picked in pos_roots_picked))\n",
        "\n",
        "\n",
        "def commuting_sl2_root_collections(start_root=0b11111111):\n",
        "  \"\"\"Yields root-collections that correspond to eight commuting SL(2)s.\"\"\"\n",
        "  pos_roots_picked = [start_root]\n",
        "  num_pos_roots_picked = 1\n",
        "  nth_root_candidates = [[] for _ in range(9)]\n",
        "  nth_root_candidates[1] = [\n",
        "      r_cand for r_cand in e8_spin16_pos_roots\n",
        "      if admissible_root(pos_roots_picked, r_cand)]\n",
        "  while num_pos_roots_picked:\n",
        "    if num_pos_roots_picked == 8:\n",
        "      yield tuple(bin(0x100 + x)[-8:] for x in pos_roots_picked)\n",
        "      num_pos_roots_picked -= 1\n",
        "      pos_roots_picked.pop()\n",
        "    remaining_candidates = nth_root_candidates[num_pos_roots_picked]\n",
        "    while remaining_candidates:\n",
        "      pos_roots_picked.append(remaining_candidates.pop())\n",
        "      num_pos_roots_picked += 1\n",
        "      nth_root_candidates[num_pos_roots_picked][:] = (\n",
        "          r_cand for r_cand in nth_root_candidates[num_pos_roots_picked - 1]\n",
        "          if admissible_root(pos_roots_picked, r_cand))\n",
        "      break\n",
        "    else:  # while-else!\n",
        "      # no more candidates with num_pos_roots_picked. Try one less.\n",
        "      num_pos_roots_picked -= 1\n",
        "      pos_roots_picked.pop()\n",
        "\n",
        "\n",
        "def hamming_distances(roots):\n",
        "  \"\"\"Computes Hamming distances for a root-set.\n",
        "\n",
        "  Args:\n",
        "    roots: Sequence of bit-strings, each of the form \"00001111\".\n",
        "\n",
        "  Returns:\n",
        "    Hamming distance statistics, as a sequence of (hamming_distance, num_pairs).\n",
        "  \"\"\"\n",
        "  s01 = str.maketrans('01', '10')\n",
        "  pos_neg_roots = [r for root in roots for r in (root, root.translate(s01))]\n",
        "  stats = {}\n",
        "  for i, j in itertools.combinations(range(len(pos_neg_roots)), 2):\n",
        "    ri = pos_neg_roots[i]\n",
        "    rj = pos_neg_roots[j]\n",
        "    dist = sum(a != b for a, b in zip(ri, rj))\n",
        "    stats[dist] = 1 + stats.get(dist, 0)\n",
        "  return sorted(stats.items())\n",
        "\n",
        "\n",
        "def do_validate_claim3():\n",
        "  \"\"\"Validates Claim #3.\"\"\"\n",
        "  print('\\n\\n### C#3: Validating the (8,4,4)-from-E8-roots-via-SL(2)x7 claim.'\n",
        "        ' ###\\n\\n')\n",
        "  violations = set()\n",
        "  for num_sol, roots in enumerate(commuting_sl2_root_collections()):\n",
        "    dists = hamming_distances(roots)\n",
        "    # In the (8, 4, 4) Hamming code, we have 8 pairs of complementary words,\n",
        "    # and each other pair has distance 4.\n",
        "    h844_dists = [(4, 112), (8, 8)]\n",
        "    is_hamming = dists == h844_dists\n",
        "    if not is_hamming: violations.add(num_sol)\n",
        "    print(f'N={num_sol}, distances={dists}, hamming={is_hamming}')\n",
        "  if not violations:\n",
        "    print('Claim 3 holds: All choices of root-sets give (8, 4, 4)-codes.')\n",
        "  else:\n",
        "    print('Claim 3 violated.')\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "fdZAIIzFT7NX",
        "cellView": "form"
      },
      "source": [
        "#@title Code for claims 5,6\n",
        "\n",
        "def check_so8c_limit(omega=0.0, r=2.0,\n",
        "                     num_spot_checks=20,\n",
        "                     seed=0,\n",
        "                     scale=0.2, threshold=0.01):\n",
        "  \"\"\"Checks that the SO(8)c potential can be found as a limit of SO(8)xSO(8).\"\"\"\n",
        "  s88 = numpy.cos(2 * omega) * r  # Spinors\n",
        "  c88 = numpy.sin(2 * omega) * r  # Co-spinors\n",
        "  tf_pot78c, tf_stat78c = get_tf_pot78c_stat78c(s88=s88, c88=c88)\n",
        "  del tf_stat78c  # Unused here, named for documentation only.\n",
        "  random_v70s = mu.rng(seed).normal(size=[num_spot_checks, 70], scale=scale)\n",
        "  max_rel_delta = float('-inf')\n",
        "  for rv70 in random_v70s:\n",
        "    pot7c, stat7c = sugra7c.potential_and_stationarity(\n",
        "        rv70 * (-.5), t_omega=mu.tff64(omega))\n",
        "    pot78c = tf_pot78c(mu.tff64(rv70)).numpy()\n",
        "    rel_delta = abs((pot78c - pot7c) / pot7c)\n",
        "    max_rel_delta = max(max_rel_delta, rel_delta)\n",
        "    print(\n",
        "        f'V_so8c = {pot7c:+15.10f}, V_so8xso8 = {pot78c:+15.10f}, '\n",
        "        f'rel_delta = {rel_delta:.6f}')\n",
        "  if max_rel_delta < threshold:\n",
        "    print('Check succeeded.')\n",
        "\n",
        "\n",
        "def do_validate_claim5():\n",
        "  \"\"\"Validates Claim #5.\"\"\"\n",
        "  print('\\n\\n ### C#5: Validating omega=0 correspondence. ###\\n\\n')\n",
        "  check_so8c_limit(omega=0, r=3.5, num_spot_checks=20, threshold=0.001)\n",
        "\n",
        "\n",
        "def do_validate_claim6():\n",
        "  \"\"\"Validates Claim #6.\"\"\"\n",
        "  print('\\n\\n### C#6: Validating omega!=0 correspondence. ###\\n\\n')\n",
        "  for omega in (-5.432, 0.035, 0.125):\n",
        "    check_so8c_limit(omega=omega, r=3.5, num_spot_checks=20, threshold=0.001)\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "nF9gPyMrUHQw"
      },
      "source": [
        "#@title Validating Claims\n",
        "\n",
        "validate_claim_1 = True #@param {type:\"boolean\"}\n",
        "validate_claim_2 = True #@param {type:\"boolean\"}\n",
        "validate_claim_3 = True #@param {type:\"boolean\"}\n",
        "validate_claim_4 = True #@param {type:\"boolean\"}\n",
        "validate_claim_5 = True #@param {type:\"boolean\"}\n",
        "validate_claim_6 = True #@param {type:\"boolean\"}\n",
        "\n",
        "if validate_claim_1: do_validate_claim1()\n",
        "if validate_claim_2: do_validate_claim2()\n",
        "if validate_claim_3: do_validate_claim3()\n",
        "if validate_claim_4: do_validate_claim4()\n",
        "if validate_claim_5: do_validate_claim5()\n",
        "if validate_claim_6: do_validate_claim6()"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ojD9S40qXZeh"
      },
      "source": [
        "The code used above to validate claims of the paper depends on a collection of library definitions that have been embedded into this colab in not-human-readable form. Since they are relevant for properly checking the correctness of all claims, running the following cell with download a ZIP archive with all the relevant library definitions."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "xZOla2wKW58l",
        "cellView": "form"
      },
      "source": [
        "#@title Download Library Code as a .ZIP (for local inspection)\n",
        "\n",
        "def _download_zip(zipname='m_theory_library.zip'):\n",
        "  import subprocess\n",
        "  from google.colab import files\n",
        "  try:\n",
        "    os.unlink(zipname)\n",
        "  except OSError:\n",
        "    pass\n",
        "  subprocess.run(['zip', '-9', '-r', zipname, 'm_theory', '-x', '*.pyc'])\n",
        "  files.download(zipname)\n",
        "\n",
        "\n",
        "_download_zip()\n"
      ],
      "execution_count": null,
      "outputs": []
    }
  ]
}